Порядок выполнения событий в DOM
Столкнулся с проблемой в своём календарике - есть два элемента, один из которых позиционируется абсолютно на весь экран, полупрозрачная затемняющая занавеска а второй - форма. Вы наверняка видели такие решения при показе картинок в lightbox или аутидентификации на habrahabr'е..
<div id='form_bg' onclick="$('form_bg').hide();$('form').hide();"> <div id='form'></div> </div>
Проблема в том, что при любом нажатии на внутренний элемент, событие автоматом вызывается на родительском элементе. Это заставило меня задуматься как над решением, так и над теорией как браузеры работают.
Две с половиной модели
Как оказывается, существует две модели передачи событий в объектно-ориентированной иерархии
- Пузырьковый метод (bubbling), когда событие возникает внутри и затем передаётся родительским элементам наружу. MS Internet Explorer, Opera, Firefox
- Захват события (capturing), событие обрабатывается сначала у родителей, а потом проникает глубже. Opera, Firefox
Консорциум W3C благоразумно решили что разработчикам может быть удобно в любую сторону направлять события (event propagation), поэтому по стандарту две модели объединены - событие сначала захватывается, а потом возвращается как пузырёк.
Таким образом разработчик может привязать вызываемый метод к фазе события:
$('form_bg').addEventListener('click',hideBackground,true); // true - говорит о фазе capturing $('form').addEventListener('click',function(){},false); // false - говорит о фазе bubbling
Получается что при клике на form_bg происходит сразу hideBackground, и form на фазе capturing не вызывается, затем возвращаясь, в фазе bubbling вызывается анонимная функция.
Традиции по умолчанию
Модель W3C приятна, но по умолчанию по всей видимости из-за IE, обычная регистрация события подразумевает bubbling фазу, т.е. изнутри наружу. А это в свою очередь значит, что если я явно укажу на родительский элемент:
$('form_bg').onclick = hideBackground; // или как выше - в <div id='form_bg' onclick=..
То любое событие без остановки вызывает у всех родительских элементов обработку onclick-события. Есть у них оно или нет, вплоть до корня - document.
Заплыв без пузырьков
В кросс-браузерном ва рианте для остановки распространения обработки события к родительским элементам, надо поменять параметр cancelBubble на true (для IE) и вызывать функцию stopPropagation (W3C модель):
function hideBackground(e){ if (!e) var e = window.event; e.cancelBubble = true; if (e.stopPropagation) e.stopPropagation(); }
Теперь мне интересно как устроена модель обработки событий в других языках и платформах - Java, NET, Flash..
Написано по мотивам статьи "Event order".